using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using XKPlatform.Common;
using System;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using System.Reflection;
using XKPlatform.Filter;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.OpenApi.Models;
using System.IO;
using Swashbuckle.AspNetCore.Swagger;
using System.Data;
using XKPlatform.DataModels;
using XKPlatform.Common.Const;
using CacheHelper;
using XKPlatform.IService;
using XKPlatform.Service;
using Chloe.Infrastructure.Interception;

using Elsa.Activities.Http.Extensions;
using Elsa.Activities.Timers.Extensions;
using Microsoft.AspNetCore.Internal;
using XKPlatform.Controllers;


using XKPlatform.Common.Enums;
using XKPlatform.DataModels.DBHelper;
using AutoCreateCodePlus;
using XKPlatform.DataModels.Dao.BaseDao;
using XKPlatform.Common.MyAttribute;

namespace XKPlatform
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;

            #region 微信相关配置读取
            GlobleConst.appid = Configuration["WeChat:appid"];
            GlobleConst.secret = Configuration["WeChat:secret"];

            GlobleConst.openidUrl = Configuration["OpenID:url"];
            GlobleConst.openidgrantType = Configuration["OpenID:grantType"];

            GlobleConst.accesstokenUrl = Configuration["AccessToken:tokenUrl"];
            GlobleConst.accesstokenType = Configuration["AccessToken:grantType"];

            //企业微信

            RedisKeyConst.QyWxAccessTokenKey = Configuration["QYWeChat:QyWxAccessTokenKey"];
            QyWxConst.QyWxAppSecret = Configuration["QYWeChat:QyWxAppSecret"];
            QyWxConst.QyWxCorpid = Configuration["QYWeChat:QyWxCorpid"];
            #endregion

            #region 网络模板地址读取
            AutoCreateCodePlus.HttpHelper.localhost = Configuration["TempUrl"];

            #endregion

#if DEBUG
            HttpContextCore.Configuration = Configuration;


            HttpContextCore.ConnectionStrings = Configuration["ConnectionStrings:DataContext"];
            HttpContextCore.developID = Configuration["developID"];
            HttpContextCore.IsScanAttribute = Convert.ToBoolean(Configuration["ScanCustomAttributeApi"]);

            DbHelper.MainDB_ConnectString = HttpContextCore.ConnectionStrings;

            GlobleConst.Host = configuration.GetSection("Host").Value;
            StaticClass.ConnStr = HttpContextCore.ConnectionStrings;
            StaticClass.DbTypeStr = "SqlServer";//默认sqlserver

            Console.WriteLine(HttpContextCore.ConnectionStrings);
#if DEBUG
            if (HttpContextCore.IsScanAttribute)
            {
                SaveAllActionsWithCustomAttribute();
            }
#endif

            IList<SysConfigModel> sysconfigs = GetSysConfigs();
            GlobleConst.RedisConStr = sysconfigs.Where(t => t.Keys == "RedisConStr").FirstOrDefault().Value;
            GlobleConst.expTime = sysconfigs.Where(t => t.Keys == "RedisExpTime").FirstOrDefault().Value.ObjectToInt();
            GlobleConst.RabbitMQHostName = sysconfigs.Where(t => t.Keys == "RabbitMQHostName").FirstOrDefault().Value;
            GlobleConst.Host = sysconfigs.Where(t => t.Keys == "Host").FirstOrDefault().Value;
            GlobleConst.IsWriteOperationLog = sysconfigs.Where(t => t.Keys == "IsWriteOperationLog").FirstOrDefault().Value.ObjectToBoolean(); // 是否写操作日志
#else
            //-----forcast
            //系统库
            HttpContextCore.ConnectionStrings = configuration.GetSection("MainConnetioStr").Value;
            DbHelper.MainDB_ConnectString = configuration.GetSection("MainConnetioStr").Value;
            //业务库
            GlobleConst.RedisConStr = configuration.GetSection("RedisConnectString").Value;//缓存
            GlobleConst.QueueConStr = configuration.GetSection("QueueConStr").Value;//队列连接
            GlobleConst.QueueName = configuration.GetSection("QueueName").Value;//队列名称
                Console.WriteLine("ConnectionStrings" + HttpContextCore.ConnectionStrings);

            GlobleConst.DbConnectionStrDic = GetDbConnectionStrDic(configuration);
#endif


            //测试mybatis
            //查单条记录



        }

        private IList<SysConfigModel> GetSysConfigs()
        {
            BaseFreeSqlDao<SysConfigModel> configDao = new BaseFreeSqlDao<SysConfigModel>();
            return configDao.GetList(o => 1 == 1);
        }
        /// <summary>
        /// 扫描标识 ApiPermissionAttribute的action 和controller  存储到sysapi
        ///  alter table [SysApi]  add     ApiSource  varchar(10)
        /// </summary>
        /// <returns></returns>
        private bool SaveAllActionsWithCustomAttribute()
        {
            //读取表中现有用户自定义的接口
            BaseFreeSqlDao<sysApiModel> sysApiDao = new BaseFreeSqlDao<sysApiModel>();
            var existLst = sysApiDao.GetList(o => o.ApiSource != ApiSourceType.sys.ToString());
            //var actionUrlLst = existLst.Select(o => o.UrlAddr).ToString();
            List<sysApiModel> apiLst = new List<sysApiModel>();
            List<sysApiModel> apiULst = new List<sysApiModel>();//需要更新的api
            var assembly = typeof(Startup).Assembly.GetTypes().AsEnumerable()
                .Where(type => typeof(Controller).IsAssignableFrom(type)).ToList();
            assembly.ForEach(d =>
            {
                var allowAttribute = d.GetCustomAttribute<ApiPermissionAttribute>();
                //controller 上增加了自定义特性
                if (allowAttribute != null)
                {
                    foreach (var method in d.GetMethods())
                    {
                        //返回类型是jsonresult 方法会增加到sysapi中，如果需要返回其他类型 需要在if中增加对应判断
                        if (method.ReturnType == typeof(JsonResult))
                        {
                            string urlAddr = string.Format("/{0}/{1}", d.Name.ToString().Replace("Controller", ""), method.Name);

                            sysApiModel apiModel = new sysApiModel();
                            var methodallowAttribute = method.GetCustomAttribute<ApiPermissionAttribute>();
                            //如果action上有特性 则以action上为准
                            if (methodallowAttribute != null)
                            {
                                apiModel.IsNeedPermisson = methodallowAttribute.isNeedPermission;
                                apiModel.description = methodallowAttribute.ApiType;
                                apiModel.ApiName = methodallowAttribute.ApiName;
                            }
                            else
                            {
                                apiModel.ApiName = method.Name;
                                apiModel.IsNeedPermisson = allowAttribute.isNeedPermission;
                                apiModel.description = allowAttribute.ApiType;
                            }

                            var upModel = existLst.Where(o => o.UrlAddr == urlAddr).FirstOrDefault();
                            if (upModel != null)
                            {
                                //存在则更新
                                upModel.IsNeedPermisson = apiModel.IsNeedPermisson;
                                upModel.description = apiModel.description;
                                upModel.ModifiedOn = DateTime.Now;
                                upModel.ApiName = apiModel.ApiName;
                                apiULst.Add(upModel);
                                continue; //存在则
                            }
                            apiModel.ID = Guid.NewGuid().ToString();
                            apiModel.PageUrl = d.Name.ToString().Replace("Controller", "");
                            apiModel.UrlAddr = urlAddr;
                            apiModel.CreatedOn = DateTime.Now.ToString();
                            apiModel.ButtonID = method.Name;
                            apiModel.ApiSource = ApiSourceType.scanAction.ToString();
                            apiLst.Add(apiModel);
                        }
                    }
                }
                else
                {
                    foreach (var methodInfo in d.GetMethods())
                    {
                        var methodallowAttribute = methodInfo.GetCustomAttribute<ApiPermissionAttribute>();
                        //controller 上没有增加特性，某些action上增加了特性
                        if (methodallowAttribute != null)
                        {
                            string urlAddr = string.Format("/{0}/{1}", d.Name.ToString().Replace("Controller", ""), methodInfo.Name);
                            var upModel = existLst.Where(o => o.UrlAddr == urlAddr).FirstOrDefault();
                            if (upModel != null)
                            {
                                //存在则更新
                                upModel.IsNeedPermisson = methodallowAttribute.isNeedPermission;
                                upModel.ModifiedOn = DateTime.Now;
                                upModel.description = methodallowAttribute.ApiType;
                                upModel.ApiName = string.IsNullOrEmpty(methodallowAttribute.ApiName) == true ? methodInfo.Name : methodallowAttribute.ApiName;
                                apiULst.Add(upModel);
                                continue; //存在则
                            }

                            sysApiModel apiModel = new sysApiModel();
                            apiModel.ID = Guid.NewGuid().ToString();
                            apiModel.UrlAddr = urlAddr;
                            apiModel.ApiName = string.IsNullOrEmpty(methodallowAttribute.ApiName) == true ? methodInfo.Name : methodallowAttribute.ApiName;
                            apiModel.PageUrl = d.Name.ToString().Replace("Controller", "");
                            apiModel.IsNeedPermisson = methodallowAttribute.isNeedPermission;
                            apiModel.description = methodallowAttribute.ApiType;
                            apiModel.CreatedOn = DateTime.Now.ToString();
                            apiModel.ButtonID = methodInfo.Name;
                            apiModel.ApiSource = ApiSourceType.scanAction.ToString();
                            apiLst.Add(apiModel);
                        }
                    }
                }
            });

            if (apiLst.Count > 0)
            {
                sysApiDao.BatchInsert(apiLst);
            }

            foreach (var itme in apiULst)
            {
                sysApiDao.Update(itme);
            }
            return true;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            //使用HttpContext单例

            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();



            //services.Configure<FormOptions>(x =>
            //{
            //    x.ValueLengthLimit = int.MaxValue;
            //    x.MultipartBodyLengthLimit = int.MaxValue;
            //    x.MemoryBufferThreshold = int.MaxValue;
            //});

            services.AddSingleton<IConfiguration>(Configuration);
            //添加自定义认证
            services.AddMvcCore(option =>
            {
                //注册参数校验过滤器
                option.Filters.Add<APIActionFilter>();
                option.Filters.Add<ExceptionFilter>();
                option.ModelMetadataDetailsProviders.Add(new RequireBindingMetadataProvider());
                //单体应用需要注册以下权限认证
                if (HttpContextCore.ServiceType == "单体")
                    option.Filters.Add(new CustomAuthorizationFilter());

            })//.AddAuthorization()
            .AddApiExplorer()
            .AddJsonFormatters(options =>
            {
                //忽略循环引用
                options.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                //不使用驼峰样式的key
                options.ContractResolver = new DefaultContractResolver();
                //设置时间格式
                options.DateFormatString = "yyyy-MM-dd HH:mm:ss.sss";
            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
            //允许一个或多个具体来源:
            services.AddCors(options =>
            {
                options.AddPolicy("app", policy =>
                {
                    policy.WithOrigins(Configuration["IdentityService:Origins"].Split(','))
                            .AllowAnyHeader()
                            .AllowAnyMethod()
                            .AllowCredentials();
                });

            });

            //注入服务

            // 使用通知服务
            // services.AddSingleton<INoticeService, NoticeService>();
            services.AddMemoryCache();
            services.AddSingleton<AutoCodeClass, AutoCodeClass>();

            string currentAssemblyName = Assembly.GetExecutingAssembly().GetName().Name;
            IDictionary<string, string> assemblyNames = new Dictionary<string, string>();
            assemblyNames.Add(currentAssemblyName, currentAssemblyName);
            BatchInjectService(assemblyNames, services);

            //swagger
            services.AddSwaggerGen(c =>
            {

                c.SwaggerDoc("v1", new OpenApiInfo { Title = "XKPlatform API", Version = "v1" });
                c.AddFluentValidationRules();
                var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
                var xmlPath = Path.Combine(basePath, "XKPlatform.xml");
                c.IncludeXmlComments(xmlPath);
            });

            // Ocelot
            //services.AddApiAuth();
            //services.AddOcelot(Configuration);


            //注入服务
            InjectService(services);

        }

        private void InjectService(IServiceCollection services)
        {


            // 使用缓存
#if DEBUG
            services.AddSingleton<ICache, RedisCache>(x => new RedisCache(GlobleConst.RedisConStr));
#else
            services.AddSingleton<ICache, AtosRedisCache>(x => new AtosRedisCache(GlobleConst.RedisConStr, GlobleConst.ClientId, GlobleConst.ClientSecret));
#endif


            // 使用消息队列
#if DEBUG
            // services.AddSingleton<IHandleMQ, HandleRabbitMQ>(x => new HandleRabbitMQ(GlobleConst.RabbitMQHostName, GlobleConst.RabbitMQPort, GlobleConst.RabbitMQUserName, GlobleConst.RabbitMQPassword, GlobleConst.RabbitMQVirtualHost, GlobleConst.RabbitMQAutomaticRecoveryEnabled, GlobleConst.RabbitMQNetworkRecoveryInterval));
#else
            services.AddSingleton<IHandleMQ, HandleServiceBus>(x => new HandleServiceBus(GlobleConst.QueueConStr));
#endif
            //设置日志参数
            LogHelper.isUsingMQ = false;//使用队列
            LogHelper.saveType = LogSaveTypeEnum.DB | LogSaveTypeEnum.File;//日志保存类型设置文件和db
            LogHelper.Env = EnvEnum.DEV;//dev环境
            LogHelper.ProjectName = "XKPlaform";//项目名




        }

        /// <summary>
        /// 批量注入服务
        /// </summary>
        /// <param name="assemblyNames"></param>
        /// <param name="services"></param>
        public void BatchInjectService(IDictionary<string, string> assemblyNames, IServiceCollection services)
        {

            Type iNeedInject = typeof(INeedInject);
            //目前只实现 Scope 类型如果需要 增加  Singleton TransentInject
            Type iScopInject = typeof(IScopeInject);
            Type iNoNeedInject = typeof(INoNeedInject);

            foreach (var assemblyItem in assemblyNames)
            {
                string assemblyInterName = assemblyItem.Key;
                string assembleObjName = assemblyItem.Key;

                Type[] interTypes = Assembly.Load(assembleObjName).GetTypes().Where(t => t.IsInterface && iNeedInject.IsAssignableFrom(t) && t != iNeedInject).ToArray();

                foreach (Type interType in interTypes)
                {
                    Type objType = Assembly.Load(assembleObjName).GetTypes().Where(t => t.IsClass && interType.IsAssignableFrom(t) && !iNoNeedInject.IsAssignableFrom(t)).FirstOrDefault();
                    if (objType == null)
                    {
                        throw new Exception($"********************当前接口={interType.Name}没有找到对应的实现类********************");
                    }
                    IList<Type> injectTypeList = objType.GetInterfaces().Where(i => i == iScopInject).ToList();
                    if (injectTypeList.Count != 1)
                    {
                        throw new Exception($"********************当前接口={interType.Name}没有找到合适的生命周期类型********************");
                    }
                    Type inJectType = injectTypeList.Single();
                    string inJectTypeName = inJectType.Name;
                    switch (inJectTypeName)
                    {
                        case "IScopeInject": services.AddScoped(interType, objType); break;
                        default: throw new Exception($"********************当前接={interType.Name}没有指定注入实例的生命周期********************");
                    }
                }
            }
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {

            //启用配置
            HttpContextCore.ServiceProvider = app.ApplicationServices;
            HttpContextCore.Env = env;
            //开启日志保存线程,必须放在HttpContextCore.ServiceProvider之后
            LogHelper.StartSaveLogThread();
            //HttpContextCore.ConnectionStrings = AtosLinkHelper.ConnectionString(HttpContextCore.Configuration["ConnectionStrings:DataContext"]);

            //app.Use(async (context, next) =>
            //{
            //    context.Features.Get<IHttpMaxRequestBodySizeFeature>()
            //        .MaxRequestBodySize = 100_000_000;

            //    await next.Invoke();
            //});

            //app.Run(async (context) =>
            //{
            //    context.Features.Get<IHttpMaxRequestBodySizeFeature>()
            //        .MaxRequestBodySize = 100_000_000;
            //});

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            //else
            //{
            //    app.UseExceptionHandler("/Error");
            ////    app.UseHsts();
            //}

            //添加编码支持
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

            app.UseCors("app");

            //单体应用 启用静态资源权限校验
            if (HttpContextCore.ServiceType == "单体")
                app.UseStaticFileMiddleWare();

            //app.UseHttpsRedirection();

            //03-27 zyf add  ���properties�ļ��ļ���֧��
            var provider = new FileExtensionContentTypeProvider();
            provider.Mappings[".properties"] = "application/octet-stream";
            app.UseStaticFiles(new StaticFileOptions
            {
                ContentTypeProvider = provider
            });

            //启用Mvc服务
            app.UseMvc(routes => routes.MapRoute(name: "default", template: "{controller=home}/{action=index}/{id?}"));
            app.UseSwagger();

            //启用中间件服务对swagger-ui，指定Swagger JSON终结点

            app.UseSwaggerUI(c =>

            {
                //typeof(ApiVersion)
                //.GetEnumNames()
                //.OrderByDescending(e => e)
                //.ToList()
                //.ForEach(version =>
                //{
                //    c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"{version}");

                //});
                //foreach (var description in provider.ApiVersionDescriptions)
                //{
                c.SwaggerEndpoint($"/swagger/v1/swagger.json", "midedn");
                //}

                //c.RoutePrefix = string.Empty;  //如果跟目录显示SwaggerUI加上这句

            });
            app.UseHttpActivities();
        }
    }

}
////https://www.cnblogs.com/yjmyzz/p/3362343.html